package cn.kennylee.codehub.common.utils.code;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.net.NetUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.Collection;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 分布式编码生成器，关注可读性，但性能不作为主要考虑。
 * <p>格式：业务前缀(1~3位) + 时间戳(15位) + 工作节点ID(2位) + 序列号(4位)</p>
 * <p>示例：ORD250718094503001234</p>
 * <p>Created on 2025/7/18.</p>
 *
 * @author kennylee
 * @since 0.0.1
 */
@Slf4j
public class DistributedCodeGenerator {

    /**
     * 去掉两位年份紧凑的时间戳格式。
     * <p>时间戳格式：yyMMddHHmmssSSS</p>
     * <p>示例：230718094503012</p>
     */
    private static final String DATE_TIME_PATTERN = "yyMMddHHmmssSSS";

    /**
     * 最大序列号 9999
     */
    private static final int MAX_SEQUENCE = 9999;

    /**
     * 最大工作节点ID 99
     */
    public static final int MAX_WORKER_ID = 99;

    /**
     * 工作节点ID (0-99)
     */
    private final int workerId;

    /**
     * 当前毫秒内的序列号
     */
    private final AtomicInteger sequence = new AtomicInteger(0);

    /**
     * 上次生成编码的时间戳
     */
    private long lastTimestamp = -1L;

    /**
     * 构造函数
     *
     * @param workerId 工作节点ID (0-99)
     */
    public DistributedCodeGenerator(int workerId) {
        if (workerId < 0 || workerId > MAX_WORKER_ID) {
            throw new IllegalArgumentException(
                String.format("Worker ID must be between 0 and %d", MAX_WORKER_ID));
        }
        this.workerId = workerId;
        log.info("初始化分布式编码生成器，工作节点ID: {}", workerId);
    }

    /**
     * 自动生成工作节点ID
     *
     * @param datacenterId 数据中心ID (0-31)
     * @param maxWorkerId  最大工作节点ID
     * @return 工作节点ID
     */
    public static int generateWorkerId(int datacenterId, int maxWorkerId) {
        if (datacenterId < 0 || datacenterId > 31) {
            throw new IllegalArgumentException("Datacenter ID must be between 0 and 31");
        }

        try {
            // 1.基于IP + PID生成
            String ip = NetUtil.getLocalhostStr();
            String[] ipSegments = ip.split("\\.");
            int ipValue = Integer.parseInt(ipSegments[3]);
            int pid = Math.toIntExact(ProcessHandle.current().pid());

            // 2.基于MAC地址（备用）
            String mac = NetUtil.getLocalMacAddress();
            int macHash = mac != null ? mac.hashCode() : 0;

            // 组合多种因素生成更稳定的workerId
            int workerId = (datacenterId * 256 + ipValue + pid + macHash) % (maxWorkerId + 1);

            // 将数据中心ID、IP地址最后一段、进程ID和MAC地址哈希值组合，确保workerId在0到maxWorkerId之间
            if (log.isTraceEnabled()) {
                log.trace("生成的Worker ID: {}, 基于数据中心ID: {}, IP地址最后一段: {}, 进程ID: {}, MAC地址哈希值: {}",
                    workerId, datacenterId, ipValue, pid, macHash);
            }

            return workerId;
        } catch (Exception e) {
            // 所有方法都失败时使用随机数
            int workerId = ThreadLocalRandom.current().nextInt(maxWorkerId + 1);
            log.warn("Failed to generate workerId, using random fallback: {}, error: {}",
                workerId, e.getMessage());
            return workerId;
        }
    }

    /**
     * 生成下一个业务编码
     *
     * @param codeType 业务编码类型
     * @return 分布式唯一编码
     */
    public synchronized String nextCode(CodeType codeType) {
        long currentTimestamp = getCurrentTimestamp();

        // 时钟回拨检测
        if (currentTimestamp < lastTimestamp) {
            long offset = lastTimestamp - currentTimestamp;
            log.error("Clock moved backwards. Refusing to generate ID for {} milliseconds", offset);
            throw new IllegalStateException(
                String.format("Clock moved backwards. Refusing to generate ID for %d milliseconds", offset));
        }

        // 同一毫秒内的序列号处理
        if (lastTimestamp == currentTimestamp) {
            int seq = sequence.incrementAndGet();
            if (seq > MAX_SEQUENCE) {
                // 当前毫秒序列号用完，等待下一毫秒
                currentTimestamp = waitNextMillis(lastTimestamp);
                sequence.set(0);
            }
        } else {
            // 时间戳变化，重置序列号
            sequence.set(0);
        }

        lastTimestamp = currentTimestamp;

        // 格式化时间戳(精确到毫秒)
        String timestampStr = DateUtil.format(DateUtil.date(currentTimestamp), DATE_TIME_PATTERN);

        // 组合生成编码
        return String.format("%s%s%02d%04d",
            codeType.getPrefix(),
            timestampStr,
            workerId,
            sequence.get());
    }

    /**
     * 批量生成编码
     *
     * @param codeType 业务编码类型
     * @param count    生成数量
     * @return 编码数组
     */
    public synchronized Collection<String> nextCodes(CodeType codeType, int count) {
        if (count <= 0 || count > 1000) {
            throw new IllegalArgumentException("Count must be between 1 and 1000");
        }

        String[] codes = new String[count];
        for (int i = 0; i < count; i++) {
            codes[i] = nextCode(codeType);
        }
        return List.of(codes);
    }

    /**
     * 获取当前时间戳
     */
    protected long getCurrentTimestamp() {
        return System.currentTimeMillis();
    }

    /**
     * 阻塞直到下一毫秒
     */
    private long waitNextMillis(long lastTimestamp) {
        long timestamp = getCurrentTimestamp();
        while (timestamp <= lastTimestamp) {
            Thread.yield();
            timestamp = getCurrentTimestamp();
        }
        return timestamp;
    }
}
